# Создать обработчик исключений с меткой handler:, реализующий три «новых системных вызова» (100, 101 и 102) для работы со «скрытыми регистрами»:
# 100 (a0 = размер). Однократно заказывает у системы память размером в a0 машинных слов, не возвращает ничего (размер и заказанный адрес запоминает в недрах обработчика).
#  Это «скрытые регистры».
# 101 (a0 = номер). Возвращает в a0 содержимое «скрытого регистра» № номер. Если номер ⩾ размер, берётся остаток от деления номер % размер (actually, всегда)).
# 102 (a0 = номер, a1 = значение). Заносит в регистр № номер % размер значение значение. 
# Это делается путём обработки исключения, проверки ucause на равенство ENVIRONMENT_CALL и содержимого a7.
# Соблюдать конвенцию неприкосновенности регистров.


.include "macros.inc"

.globl	main
.text
main:	la	t1 handler	# Сохранение адреса обработчика исключения в utvec
	csrw	t1 utvec
	csrsi	ustatus 1	# Разрешить обработку исключений (бит 0 в регистре ustatus)

        # Считать N и выделить N скрытых регистров
        li      a7 5
        ecall
        li      a7 100
        ecall

        # Считать номер
rloop:  li      a7 5
        ecall
        beqz    a0 rdone
        bltz    a0 rread
        # положительный — это запись
        mv      t0 a0
        li      a7 5
        ecall
        mv      a1 a0
        mv      a0 t0
        li      a7 102
        ecall
        b       rloop
rread:  neg     a0 a0
        li      a7 101
        ecall
        li      a7 1
        ecall
        li      a0 '\n'
        li      a7 11
        ecall
        b       rloop

rdone:	li	a7 10
	ecall

.data
h_a0:   .space  4
h_a7:   .space  4
h_t1:   .space  4
sizer:	.space	4
addrr:	.space	4
.text
handler: 
	csrw    t0 uscratch     	# Сохраним t0
        sw      a0 h_a0 t0      	# Сохраним a0
        sw      a7 h_a7 t0      	# Сохраним a7
        sw      t1 h_t1 t0      	# Сохраним t1
                
        csrr    t0 ucause       	# Прочтём причину исключения
        li	t1 8
        bne	t0 t1 anothercall	# Если это не ENVIRONMENT_CALL
        
        li	t1 100			# Проверим номер ecall
        beq	a7 t1 call100
        li	t1 101
        beq	a7 t1 call101
        li	t1 102
        beq	a7 t1 call102
        b	anothercall

call100:			# Закажем у системы память размером в a0 машинных слов и сохраним размер и адрес
	sw      a0 sizer t0
	slli	a0 a0 2 
	syscall 9 a0            # Закажем память (объём уже в a0)
	sw      a0 addrr t0
	lw      a0 h_a0         # Восстановим a0	
	b	hdone
		        
call101:			# Вернем в a0 содержимое «скрытого регистра» № в a0. Если номер ⩾ размер, берётся остаток от деления номер % размер
	lw      t0 sizer
	lw	t1 addrr
	blt	a0 t0 lval
	rem	a0 a0 t0        
lval:	mv	t0 a0
	slli	t0 t0 2
	add	t0 t1 t0
	lw	a0 (t0)
	b	hdone
		                        
call102:			# Занесем в регистр № в a0 % размер значение в a1
	lw      t0 sizer
	lw	t1 addrr
	blt	a0 t0 sval
	rem	a0 a0 t0        
sval:	mv	t0 a0
	slli	t0 t0 2
	add	t0 t1 t0
	sw	a1 (t0)
	lw      a0 h_a0         # Восстановим a0
	b	hdone	        

anothercall:
	lw      a0 h_a0         # Восстановим a0
	csrsi	ustatus 0	# Запретить обработку исключений (бит 0 в регистре ustatus) 
	b	exit     
        
hdone:  csrr    t0 uepc         # Адрес прерванной инструкции        
        addi    t0 t0 4         # Адрес следующей инструкции
        csrw    t0 uepc         # Запишем его
        
exit:   lw      a7 h_a7         # Восстановим a7
	lw      t1 h_t1         # Восстановим t1
        csrr    t0 uscratch     # Восстановим t0
        uret
